安装依赖,这里请注意版本
yarn add monaco-editor@0.29.1yarn add monaco-editor-webpack-plugin@5.0.0配置 webpack 插件
// vue.config.js...const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')module.export = { ... configureWebpack: {name: name,resolve: { alias: {'@': resolve('src'), },},plugins: [new MonacoWebpackPlugin()], }, ...}请注意 monaco-editor-webpack-plugin 和 monaco-editor 的对应关系,否则可能会出现无法运行的情况。
monaco-editor-webpack-pluginmonaco-editor 7.*.*>= 0.31.06.*.*0.30.*5.*.*0.29.*4.*.*0.25.*, 0.26.*, 0.27.*, 0.28.*3.*.*0.22.*, 0.23.*, 0.24.*2.*.*0.21.*1.9.*0.20.*1.8.*0.19.*1.7.*0.18.*简易 SQL 编辑器先上干货!
import * as monaco from 'monaco-editor'/** * VS Code 编辑器 * * 通过 getEditorVal 函数向外传递编辑器即时内容 * 通过 initValue 用于初始化编辑器内容。 * 编辑器默认 sql 语言,支持的语言请参考 node_modules\monaco-editor\esm\vs\basic-languages 目录下~ * 编辑器样式仅有'vs', 'vs-dark', 'hc-black' 三种 */export default { name: 'MonacoEditor', props: {initValue: { type: String, default: '',},readOnly: Boolean,language: { type: String, default: 'sql',},height: { type: Number, default: 300,},theme: { type: String, default: 'vs',}, }, data() {return { monacoEditor: null, // 语言编辑器} }, computed: {inputVal() { return this.monacoEditor?.getValue()}, }, watch: {inputVal() { if (this.monacoEditor) {this.$emit('change', this.monacoEditor.getValue()) }},theme() { this.setTheme(this.theme)},height() { this.layout()}, }, mounted() {this.initEditor() }, beforeDestroy() {if (this.monacoEditor) { this.monacoEditor.dispose()} }, methods: {initEditor() { if (this.$refs.codeContainer) {this.registerCompletion()// 初始化编辑器,确保dom已经渲染this.monacoEditor = monaco.editor.create(this.$refs.codeContainer, { value: '', // 编辑器初始显示文字 language: 'sql', // 语言 readOnly: this.readOnly, // 是否只读 Defaults to false | true automaticLayout: true, // 自动布局 theme: this.theme, // 官方自带三种主题vs, hc-black, or vs-dark minimap: {// 关闭小地图enabled: false, }, tabSize: 2, // tab缩进长度}) } this.setInitValue()},focus() { this.monacoEditor.focus()},layout() { this.monacoEditor.layout()},getValue() { return this.monacoEditor.getValue()},// 将 initValue Property 同步到编辑器中setInitValue() { this.monacoEditor.setValue(this.initValue)},setTheme() { monaco.editor.setTheme(this.theme)},getSelectionVal() { const selection = this.monacoEditor.getSelection() // 获取光标选中的值 const { startLineNumber, endLineNumber, startColumn, endColumn } = selection const model = this.monacoEditor.getModel() return model.getValueInRange({startLineNumber,startColumn,endLineNumber,endColumn, })},setPosition(column, lineNumber) { this.monacoEditor.setPosition({ column, lineNumber })},getPosition() { return this.monacoEditor.getPosition()}, },}相关功能获取选中代码getSelectionVal() { const selection = this.monacoEditor.getSelection() // 获取光标选中的值 const { startLineNumber, endLineNumber, startColumn, endColumn } = selection const model = this.monacoEditor.getModel() return model.getValueInRange({startLineNumber,startColumn,endLineNumber,endColumn, })},替换选中代码insertStringInTemplate(str) { const selection = this.monacoEditor.getSelection() // 获取光标选中的值 const { startLineNumber, endLineNumber, startColumn, endColumn } = selection const model = this.monacoEditor.getModel() const textBeforeSelection = model.getValueInRange({startLineNumber: 1,startColumn: 0,endLineNumber: startLineNumber,endColumn: startColumn, }) const textAfterSelection = model.getValueInRange({startLineNumber: endLineNumber,startColumn: endColumn,endLineNumber: model.getLineCount(),endColumn: model.getLineMaxColumn(model.getLineCount()), }) this.monacoEditor.setValue(textBeforeSelection + str + textAfterSelection) this.monacoEditor.focus() this.monacoEditor.setPosition({lineNumber: startLineNumber,column: startColumn + str.length, })},处理光标位置 setPosition(column, lineNumber) { this.monacoEditor.setPosition({ column, lineNumber })},getPosition() { return this.monacoEditor.getPosition()},自定义 SQL 库表提示,并保留原有 SQL 提示首先由后端提供具体的库表信息:
export const hintData = { adbs: ['dim_realtime_recharge_paycfg_range', 'dim_realtime_recharge_range'], dimi: ['ads_adid', 'ads_spec_adid_category'],}然后根据已有库表信息进行自定义 AutoComplete
import * as monaco from 'monaco-editor'import { language } from 'monaco-editor/esm/vs/basic-languages/sql/sql'const { keywords } = languageexport default { ... mounted() {this.initEditor() }, methods: {...registerCompletion() { const _that = this monaco.languages.registerCompletionItemProvider('sql', {triggerCharacters: ['.', ...keywords],provideCompletionItems: (model, position) => { let suggestions = [] const { lineNumber, column } = position const textBeforePointer = model.getValueInRange({startLineNumber: lineNumber,startColumn: 0,endLineNumber: lineNumber,endColumn: column, }) const tokens = textBeforePointer.trim().split(/\s+/) const lastToken = tokens[tokens.length - 1] // 获取最后一段非空字符串 if (lastToken.endsWith('.')) {const tokenNoDot = lastToken.slice(0, lastToken.length - 1)if (Object.keys(_that.hintData).includes(tokenNoDot)) { suggestions = [..._that.getTableSuggest(tokenNoDot)]} } else if (lastToken === '.') {suggestions = [] } else {suggestions = [..._that.getDBSuggest(), ..._that.getSQLSuggest()] } return {suggestions, }}, })},// 获取 SQL 语法提示getSQLSuggest() { return keywords.map((key) => ({label: key,kind: monaco.languages.CompletionItemKind.Enum,insertText: key, }))},getDBSuggest() { return Object.keys(this.hintData).map((key) => ({label: key,kind: monaco.languages.CompletionItemKind.Constant,insertText: key, }))},getTableSuggest(dbName) { const tableNames = this.hintData[dbName] if (!tableNames) {return [] } return tableNames.map((name) => ({label: name,kind: monaco.languages.CompletionItemKind.Constant,insertText: name, }))},initEditor() { if (this.$refs.codeContainer) {this.registerCompletion()// 初始化编辑器,确保dom已经渲染this.monacoEditor = monaco.editor.create(this.$refs.codeContainer, { value: '', // 编辑器初始显示文字 language: 'sql', // 语言 readOnly: this.readOnly, // 是否只读 Defaults to false | true automaticLayout: true, // 自动布局 theme: this.theme, // 官方自带三种主题vs, hc-black, or vs-dark minimap: {// 关闭小地图enabled: false, }, tabSize: 2, // tab缩进长度}) } this.setValue(this.value)}, }}编辑器 resizeresize() { this.monacoEditor.layout()},编辑器设置主题注意!设置主题并非在编辑器实例上修改的哦!
setTheme() { monaco.editor.setTheme(this.theme)},SQL 代码格式化编辑器自身不支持 sql 格式化(试了下 JavaScript 是支持的),所以用到了 sql-formatter 这个库。
import { format } from 'sql-formatter'...format() { this.monacoEditor.setValue(format(this.monacoEditor.getValue(), { indentStyle: 'tabularLeft',}), )},...右键菜单汉化需要安装以下两个库
npm install monaco-editor-nls --savenpm install monaco-editor-esm-webpack-plugin --save-dev具体用法可以直接去 https://www.npmjs.com/package/monaco-editor-esm-webpack-plugin 里面看,我就不搬运了~
记得销毁编辑器对象哦 beforeDestroy() {if (this.monacoEditor) { this.monacoEditor.dispose()} },踩坑下面是我遇到的几个坑。
最新版本的 Monaco Editor 已经使用了 ES2022 的语法,所以老项目可能会出现编译不过的问题。所以我把版本调低了一些。在最初调试编辑器的时候出现了无法编辑的情况,后来发现是同事用到了 default-passive-events 这个库来关闭 chrome 的 Added non-passive event listener to a scroll-blocking event. Consider marking event handler as 'passive' to make the page more responsive 警告。结果拦截一些 event。如何快速去看懂 Monaco Editor一开始我看它的官方文档是非常懵的,各种接口、函数、对象的定义,完全不像是个前端库那么好理解。鼓捣了好久才慢慢找到门路。
先看示例
查看它的 playground,上面其实是有一些功能可以直接找到的。查看它在 github 上的 /samples 目录,里面也有不少示例。
去掘金这类网站上找别人写的示例,能有不少启发。再看 API
了解了自己所需要的功能相关的代码,再去看它文档的 API 就会发现容易理解多了。逐步发散理解